#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <strings.h>
#include <functional>
#include <memory>
#include <cstdio>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstdlib>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>

namespace client {
    using namespace std;

    enum {
        SUCCESS,
        USAGE_ERR,
        SOCKET_ERR,
        BIND_ERR
    };

    class UDPClient {
    private:
        int _sockfd;
        string _server_ip;
        uint16_t _server_port;
        bool _quit;

    public:
        UDPClient(const string& server_ip, const uint16_t& server_port)
            : _sockfd(-1)
            , _server_ip(server_ip)
            , _server_port(server_port)
            , _quit(false)
        {}

    public:
        static void usage(const string& process) {
            cout << "\n\tusage: " << process << " server_ip server_port" << endl << endl;
        }

        void initClient() {
            //todo 创建socket
            _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
            if (_sockfd == -1) {
                perror("socket error");
                exit(SOCKET_ERR);
            }

            cout << "socket success! sockfd: " << _sockfd << endl;

            //? 客户端的socket需要绑定吗？ 那肯定是需要绑定的
            //* 但是不需要我们手动绑定，因为客户端的socket不需要特定的不会改变的端口
            //* 只需要有一个端口来发送消息就好了，因此不需要程序员来手动绑定特定的一个端口
            //* 让操作系统随机分配一个端口来用就行了，操作系统的分配发生在第一次发送消息的时候
            //todo 操作系统自行绑定 ...
        }

        void run() {
            struct sockaddr_in server;
            memset(&server, 0, sizeof(server));
            server.sin_family = AF_INET;
            server.sin_port = htons(_server_port);
            server.sin_addr.s_addr = inet_addr(_server_ip.c_str());

            string message;
            char buffer[1024];
            bzero(buffer, sizeof(buffer));

            while(!_quit) {
                // cout << "please enter# ";
                // cin >> message;

                // sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));

                // struct sockaddr_in server;
                // socklen_t len = sizeof(server); 
                // size_t s = recvfrom(_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&server, &len);
                // if (s >= 0) buffer[s] = '\0';
                // cout << message << " # " << buffer << endl;

                fprintf(stderr, "Enter# ");
                fflush(stderr);
                fgets(buffer, sizeof(buffer) - 1, stdin);
                buffer[strlen(buffer) - 1] = '\0'; // 将'\n'换成'\0'
                message = string(buffer);
                sendto(_sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));
            }
        }

    private:
        static void* readMessage(void* args) {
            int sockfd = *(static_cast<int*>(args));
            pthread_detach(pthread_self()); // 让线程执行完任务后直接退出，让操作系统来回收资源，不需要主线程等待

            while (true) {
                char buffer[1024] = {};
                struct sockaddr_in others;
                socklen_t len = sizeof(others);

                size_t s = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&others, &len);
                if(s >= 0) buffer[s] = '\0';
                cout << buffer << endl;
            }

            return nullptr;
        }
        
    }; //@end class UDPClient
} //@ end namespace client